/*
 * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
 * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

using System;
using System.Linq;
using QuantConnect.Brokerages;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;
using QuantConnect.Orders;
using QuantConnect.Securities;

namespace QuantConnect.Algorithm.CSharp
{
    /// <summary>
    ///
    /// QCU: Opening Breakout Algorithm
    ///
    /// In this algorithm we attempt to provide a working algorithm that
    /// addresses many of the primary algorithm concerns. These concerns
    /// are:
    ///
    ///     1. Signal Generation.
    ///             This algorithm aims to generate signals for an opening
    ///             breakout move before 10am. Signals are generated by
    ///             producing the opening five minute bar, and then trading
    ///             in the direction of the breakout from that bar.
    ///
    ///     2. Position Sizing.
    ///             Positions are sized using recently the average true range.
    ///             The higher the recently movement, the smaller position.
    ///             This helps to reduce the risk of losing a lot on a single
    ///             transaction.
    ///
    ///     3. Active Stop Loss.
    ///             Stop losses are maintained at a fixed global percentage to
    ///             limit maximum losses per day, while also a trailing stop
    ///             loss is implemented using the parabolic stop and reverse
    ///             in order to gauge exit points.
    ///
    /// </summary>
    /// <meta name="tag" content="strategy example" />
    /// <meta name="tag" content="indicators" />
    public class OpeningBreakoutAlgorithm : QCAlgorithm
    {
#pragma warning disable 00162 // File contains unreachable code when EnableOrderUpdateLogging is false

        // the equity symbol we're trading
        private const string symbol = "SPY";

        // plotting and logging control
        private const bool EnablePlotting = true;
        private const bool EnableOrderUpdateLogging = false;
        private const int PricePlotFrequencyInSeconds = 15;

        // risk control
        private const decimal MaximumLeverage = 4;
        private const decimal GlobalStopLossPercent = 0.001m;
        private const decimal PercentProfitStartPsarTrailingStop = 0.0003m;
        private const decimal MaximumPorfolioRiskPercentPerPosition = .0025m;

        // entrance criteria
        private const int OpeningSpanInMinutes = 3;
        private const decimal BreakoutThresholdPercent = 0.00005m;
        private const decimal AtrVolatilityThresholdPercent = 0.00275m;
        private const decimal StdVolatilityThresholdPercent = 0.005m;

        // this is the security we're trading
        private Security _security;

        // define our indicators used for trading decisions
        private AverageTrueRange ATR14;
        private StandardDeviation STD14;
        private AverageDirectionalIndex ADX14;
        private ParabolicStopAndReverse PSARMin;

        // smoothed values
        private ExponentialMovingAverage _smoothedSTD14;
        private ExponentialMovingAverage _smoothedATR14;

        // working variable to control our algorithm

        // this flag is used to run some code only once after the algorithm is warmed up
        private bool FinishedWarmup;
        // this is used to record the last time we closed a position
        private DateTime LastExitTime;
        // this is our opening n minute bar
        private TradeBar OpeningBarRange;
        // this is the ticket from our market order (entrance)
        private OrderTicket MarketTicket;
        // this is the ticket from our stop loss order (exit)
        private OrderTicket StopLossTicket;
        // this flag is used to indicate we've switched from a global, non changing
        // stop loss to a dynamic trailing stop using the PSAR
        private bool EnablePsarTrailingStop;

        /// <summary>
        /// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
        /// </summary>
        public override void Initialize()
        {
            // initialize algorithm level parameters
            SetStartDate(2013, 10, 07);
            SetEndDate(2013, 10, 11);
            //SetStartDate(2014, 01, 01);
            //SetEndDate(2014, 06, 01);
            SetCash(100000);

            // leverage tradier $1 traders
            SetBrokerageModel(BrokerageName.TradierBrokerage);

            // request high resolution equity data
            AddSecurity(SecurityType.Equity, symbol, Resolution.Second);

            // save off our security so we can reference it quickly later
            _security = Securities[symbol];

            // Set our max leverage
            _security.SetLeverage(MaximumLeverage);

            // define our longer term indicators
            ADX14 = ADX(symbol, 28, Resolution.Hour);
            STD14 = STD(symbol, 14, Resolution.Daily);
            ATR14 = ATR(symbol, 14, resolution: Resolution.Daily);
            PSARMin = new ParabolicStopAndReverse(symbol, afStart: 0.0001m, afIncrement: 0.0001m);

            // smooth our ATR over a week, we'll use this to determine if recent volatilty warrants entrance
            var oneWeekInMarketHours = (int)(5*6.5);
            _smoothedATR14 = new ExponentialMovingAverage("Smoothed_" + ATR14.Name, oneWeekInMarketHours).Of(ATR14);
            // smooth our STD over a week as well
            _smoothedSTD14 = new ExponentialMovingAverage("Smoothed_"+STD14.Name, oneWeekInMarketHours).Of(STD14);

            // initialize our charts
            var chart = new Chart(symbol);
            chart.AddSeries(new Series(ADX14.Name, SeriesType.Line, 0));
            chart.AddSeries(new Series("Enter", SeriesType.Scatter, 0));
            chart.AddSeries(new Series("Exit", SeriesType.Scatter, 0));
            chart.AddSeries(new Series(PSARMin.Name, SeriesType.Scatter, 0));
            AddChart(chart);

            var history = History(symbol, 20, Resolution.Daily);
            foreach (var bar in history)
            {
                ADX14.Update(bar);
                ATR14.Update(bar);
                STD14.Update(bar.EndTime, bar.Close);
            }

            // schedule an event to run every day at five minutes after our symbol's market open
            Schedule.Event("MarketOpenSpan")
                .EveryDay(symbol)
                .AfterMarketOpen(symbol, minutesAfterOpen: OpeningSpanInMinutes)
                .Run(MarketOpeningSpanHandler);

            Schedule.Event("MarketOpen")
                .EveryDay(symbol)
                .AfterMarketOpen(symbol, minutesAfterOpen: -1)
                .Run(() => PSARMin.Reset());
        }

        /// <summary>
        /// This function is scheduled to be run every day at the specified number of minutes after market open
        /// </summary>
        public void MarketOpeningSpanHandler()
        {
            // request the last n minutes of data in minute bars, we're going to
            // define the opening rang
            var history = History(symbol, OpeningSpanInMinutes, Resolution.Minute);

            // this is our bar size
            var openingSpan = TimeSpan.FromMinutes(OpeningSpanInMinutes);

            // we only care about the high and low here
            OpeningBarRange = new TradeBar
            {
                // time values
                Time = Time - openingSpan,
                EndTime = Time,
                Period = openingSpan,
                // high and low
                High = _security.Close,
                Low = _security.Close
            };

            // aggregate the high/low for the opening range
            foreach (var tradeBar in history)
            {
                OpeningBarRange.Low = Math.Min(OpeningBarRange.Low, tradeBar.Low);
                OpeningBarRange.High = Math.Max(OpeningBarRange.High, tradeBar.High);
            }

            // widen the bar when looking for breakouts
            OpeningBarRange.Low *= 1 - BreakoutThresholdPercent;
            OpeningBarRange.High *= 1 + BreakoutThresholdPercent;

            Log("---------" + Time.Date + "---------");
            Log("OpeningBarRange: Low: " + OpeningBarRange.Low.SmartRounding() + " High: " + OpeningBarRange.High.SmartRounding());
        }

        /// <summary>
        /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
        /// </summary>
        /// <param name="data">Slice object keyed by symbol containing the stock data</param>
        public override void OnData(Slice data)
        {
            // we don't need to run any of this during our warmup phase
            if (IsWarmingUp) return;

            // when we're done warming up, register our indicators to start plotting
            if (!IsWarmingUp && !FinishedWarmup)
            {
                // this is a run once flag for when we're finished warmup
                FinishedWarmup = true;

                // plot our hourly indicators automatically, wait for them to ready
                PlotIndicator("ADX", ADX14);
                PlotIndicator("ADX", ADX14.NegativeDirectionalIndex, ADX14.PositiveDirectionalIndex);

                PlotIndicator("ATR", true, ATR14);
                PlotIndicator("STD", true, STD14);
                PlotIndicator("ATR", true, _smoothedATR14);
            }

            // update our PSAR
            PSARMin.Update((TradeBar) _security.GetLastData());

            // plot price until an hour after we close so we can see our execution skillz
            if (ShouldPlot)
            {
                // we can plot price more often if we want
                Plot(symbol, "Price", _security.Close);
                // only plot psar on the minute
                if (PSARMin.IsReady)
                {
                    Plot(symbol, PSARMin);
                }
            }

            // first wait for our opening range bar to be set to today
            if (OpeningBarRange == null || OpeningBarRange.EndTime.Date != Time.Date || OpeningBarRange.EndTime == Time) return;

            // we only trade max once per day, so if we've already exited the stop loss, bail
            if (StopLossTicket != null && StopLossTicket.Status == OrderStatus.Filled)
            {
                // null these out to signal that we're done trading for the day
                OpeningBarRange = null;
                StopLossTicket = null;
                return;
            }

            // now that we have our opening bar, test to see if we're already in a position
            if (!_security.Invested)
            {
                ScanForEntrance();
            }
            else
            {
                // if we haven't exited yet then manage our stop loss, this controls our exit point
                if (_security.Invested)
                {
                    ManageStopLoss();
                }
                else if (StopLossTicket != null && StopLossTicket.Status.IsOpen())
                {
                    StopLossTicket.Cancel();
                }
            }
        }

        /// <summary>
        /// Scans for a breakout from the opening range bar
        /// </summary>
        private void ScanForEntrance()
        {
            // scan for entrances, we only want to do this before 10am
            if (Time.TimeOfDay.Hours >= 10) return;

            // expect capture 10% of the daily range
            var expectedCaptureRange = 0.1m*ATR14;

            var allowedDollarLoss = MaximumPorfolioRiskPercentPerPosition * Portfolio.TotalPortfolioValue;

            var shares = (int) (allowedDollarLoss/expectedCaptureRange);

            // determine a position size based on an acceptable loss in proporton to our total portfolio value
            //var shares = (int) (MaximumLeverage*MaximumPorfolioRiskPercentPerPosition*Portfolio.TotalPortfolioValue/(0.4m*ATR14));

            // max out at a little below our stated max, prevents margin calls and such
            var maxShare = (int) CalculateOrderQuantity(symbol, MaximumLeverage);
            shares = Math.Min(shares, maxShare);

            // min out at 1x leverage
            //var minShare = CalculateOrderQuantity(symbol, MaximumLeverage/2m);
            //shares = Math.Max(shares, minShare);

            // we're looking for a breakout of the opening range bar in the direction of the medium term trend
            if (ShouldEnterLong)
            {
                // breakout to the upside, go long (fills synchronously)
                MarketTicket = MarketOrder(symbol, shares);
                Log("Enter long @ " + MarketTicket.AverageFillPrice.SmartRounding() + " Shares: " + shares);
                Plot(symbol, "Enter", MarketTicket.AverageFillPrice);

                // we'll start with a global, non-trailing stop loss
                EnablePsarTrailingStop = false;

                // submit stop loss order for max loss on the trade
                var stopPrice = _security.Low*(1 - GlobalStopLossPercent);
                StopLossTicket = StopMarketOrder(symbol, -shares, stopPrice);
                if (EnableOrderUpdateLogging)
                {
                    Log("Submitted stop loss @ " + stopPrice.SmartRounding());
                }
            }
            else if (ShouldEnterShort)
            {
                // breakout to the downside, go short
                MarketTicket = MarketOrder(symbol, - -shares);
                Log("Enter short @ " + MarketTicket.AverageFillPrice.SmartRounding());
                Plot(symbol, "Enter", MarketTicket.AverageFillPrice);

                // we'll start with a global, non-trailing stop loss
                EnablePsarTrailingStop = false;

                // submit stop loss order for max loss on the trade
                var stopPrice = _security.High*(1 + GlobalStopLossPercent);
                StopLossTicket = StopMarketOrder(symbol, -shares, stopPrice);
                if (EnableOrderUpdateLogging)
                {
                    Log("Submitted stop loss @ " + stopPrice.SmartRounding() + " Shares: " + shares);
                }
            }
        }

        /// <summary>
        /// Manages our stop loss ticket
        /// </summary>
        private void ManageStopLoss()
        {
            // if we've already exited then no need to do more
            if (StopLossTicket == null || StopLossTicket.Status == OrderStatus.Filled) return;

            // only do this once per minute
            //if (Time.RoundDown(TimeSpan.FromMinutes(1)) != Time) return;

            // get the current stop price
            var stopPrice = StopLossTicket.Get(OrderField.StopPrice);

            // check for enabling the psar trailing stop
            if (ShouldEnablePsarTrailingStop(stopPrice))
            {
                EnablePsarTrailingStop = true;
                Log("Enabled PSAR trailing stop @ ProfitPercent: " + _security.Holdings.UnrealizedProfitPercent.SmartRounding());
            }

            // we've trigger the psar trailing stop, so start updating our stop loss tick
            if (EnablePsarTrailingStop && PSARMin.IsReady)
            {
                StopLossTicket.Update(new UpdateOrderFields {StopPrice = PSARMin});
                Log("Submitted stop loss @ " + PSARMin.Current.Value.SmartRounding());
            }
        }

        /// <summary>
        /// This event handler is fired for each and every order event the algorithm
        /// receives. We'll perform some logging and house keeping here
        /// </summary>
        public override void OnOrderEvent(OrderEvent orderEvent)
        {
            // print debug messages for all order events
            if (LiveMode || orderEvent.Status.IsFill() || EnableOrderUpdateLogging)
            {
                LiveDebug("Filled: " + orderEvent.FillQuantity + " Price: " + orderEvent.FillPrice);
            }

            // if this is a fill and we now don't own any stock, that means we've closed for the day
            if (!_security.Invested && orderEvent.Status == OrderStatus.Filled)
            {
                // reset values for tomorrow
                LastExitTime = Time;
                var ticket = Transactions.GetOrderTickets(x => x.OrderId == orderEvent.OrderId).Single();
                Plot(symbol, "Exit", ticket.AverageFillPrice);
            }
        }

        /// <summary>
        /// If we're still invested by the end of the day, liquidate
        /// </summary>
        public override void OnEndOfDay(Symbol symbol)
        {
            if (symbol == _security.Symbol && _security.Invested)
            {
                Liquidate();
            }
        }

        /// <summary>
        /// Determines whether or not we should plot. This is used
        /// to provide enough plot points but not too many, we don't
        /// need to plot every second in backtests to get an idea of
        /// how good or bad our algorithm is performing
        /// </summary>
        public bool ShouldPlot
        {
            get
            {
                // always in live
                if (LiveMode) return true;
                // set in top to override plotting during long backtests
                if (!EnablePlotting) return false;
                // every 30 seconds in backtest
                if (Time.RoundDown(TimeSpan.FromSeconds(PricePlotFrequencyInSeconds)) != Time) return false;
                // always if we're invested
                if (_security.Invested) return true;
                // always if it's before noon
                if (Time.TimeOfDay.Hours < 10.25) return true;
                // for an hour after our exit
                if (Time - LastExitTime < TimeSpan.FromMinutes(30)) return true;

                return false;
            }
        }

        /// <summary>
        /// In live mode it's nice to push messages to the debug window
        /// as well as the log, this allows easy real time inspection of
        /// how the algorithm is performing
        /// </summary>
        public void LiveDebug(object msg)
        {
            if (msg == null) return;

            if (LiveMode)
            {
                Debug(msg.ToString());
                Log(msg.ToString());
            }
            else
            {
                Log(msg.ToString());
            }
        }

        /// <summary>
        /// Determines whether or not we should end a long position
        /// </summary>
        private bool ShouldEnterLong
        {
            // check to go in the same direction of longer term trend and opening break out
            get
            {
                return IsUptrend
                    && HasEnoughRecentVolatility
                    && _security.Close > OpeningBarRange.High;
            }
        }

        /// <summary>
        /// Determines whether or not we're currently in a medium term up trend
        /// </summary>
        private bool IsUptrend
        {
            get { return ADX14 > 20 && ADX14.PositiveDirectionalIndex > ADX14.NegativeDirectionalIndex; }
        }

        /// <summary>
        /// Determines whether or not we should enter a short position
        /// </summary>
        private bool ShouldEnterShort
        {
            // check to go in the same direction of longer term trend and opening break out
            get
            {
                return IsDowntrend
                    && HasEnoughRecentVolatility
                    && _security.Close < OpeningBarRange.Low;
            }
        }

        /// <summary>
        /// Determines whether or not we're currently in a medium term down trend
        /// </summary>
        private bool IsDowntrend
        {
            get { return ADX14 > 20 && ADX14.NegativeDirectionalIndex > ADX14.PositiveDirectionalIndex; }
        }

        /// <summary>
        /// Determines whether or not there's been enough recent volatility for
        /// this strategy to work
        /// </summary>
        private bool HasEnoughRecentVolatility
        {
            get
            {
                return _smoothedATR14 > _security.Close*AtrVolatilityThresholdPercent
                    || _smoothedSTD14 > _security.Close*StdVolatilityThresholdPercent;
            }
        }

        /// <summary>
        /// Determines whether or not we should enable the psar trailing stop
        /// </summary>
        /// <param name="stopPrice">current stop price of our stop loss tick</param>
        private bool ShouldEnablePsarTrailingStop(decimal stopPrice)
        {
            // no need to enable if it's already enabled
            return !EnablePsarTrailingStop
                // once we're up a certain percentage, we'll use PSAR to control our stop
                && _security.Holdings.UnrealizedProfitPercent > PercentProfitStartPsarTrailingStop
                // make sure the PSAR is on the right side
                && PsarIsOnRightSideOfPrice
                // make sure the PSAR is more profitable than our global loss
                && IsPsarMoreProfitableThanStop(stopPrice);
        }

        /// <summary>
        /// Determines whether or not the PSAR is on the right side of price depending on our long/short
        /// </summary>
        private bool PsarIsOnRightSideOfPrice
        {
            get
            {
                return (_security.Holdings.IsLong && PSARMin < _security.Close)
                    || (_security.Holdings.IsShort && PSARMin > _security.Close);
            }
        }

        /// <summary>
        /// Determines whether or not the PSAR stop price is better than the specified stop price
        /// </summary>
        private bool IsPsarMoreProfitableThanStop(decimal stopPrice)
        {
            return (_security.Holdings.IsLong && PSARMin > stopPrice)
                || (_security.Holdings.IsShort && PSARMin < stopPrice);
        }
#pragma warning restore 00162
    }
}
